package com.information.manage.basic.common.utils.zip;

import java.io.*;
import java.nio.charset.Charset;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * zip解析器
 */
public class ZipParser implements Closeable {

    private ZipFile zipFile;

    private File tempFile;

    /**
     * key：文件类型
     * value: list
     */
    private Map<String, List<ZipEntry>> entryMap = new HashMap<>();

    public Map<String, List<ZipEntry>> getEntryMap() {
        return entryMap;
    }

    public ZipParser(InputStream source, int maxEntrySize) throws IOException, TooMuchEntryException {
        // entry 个数计数
        // zip临时文件
        tempFile = File.createTempFile("cloud_", ".zip");
        FileOutputStream zipOutput = new FileOutputStream(tempFile);
        byte[] buffer = new byte[2048];
        int count = 0;
        while((count = source.read(buffer)) != - 1){
            zipOutput.write(buffer, 0, count);
        }
        zipOutput.close();

        // 获取zip条目
        try {
            parse(maxEntrySize, Charset.forName("UTF-8"));
        }catch (IllegalArgumentException e){
            // 不能处理utf-8字符集，字符集变为gbk
            parse(maxEntrySize, Charset.forName("GBK"));
        }
    }

    private void parse(int maxEntrySize, Charset charset) throws IOException, TooMuchEntryException {
        int entryCount = 0;
        zipFile = new ZipFile(tempFile, charset);
        Enumeration enumerations = zipFile.entries();
        while(enumerations.hasMoreElements()){
            ZipEntry entry = (ZipEntry) enumerations.nextElement();
            String name = entry.getName();
            int index = name.lastIndexOf(".");
            String key = null;
            if(index == -1){
                // 不存在，文件名称就是文件类型
                if(entry.isDirectory()){
                    continue;
                }
                // 获取文件分割符
                index = name.lastIndexOf("/");
                if(index == -1) key = name;
                else key = name.substring(index + 1, name.length());
            }else{
                // 存在，获取文件类型
                key = name.substring(index + 1, name.length());
            }
            // 保存条目
            List<ZipEntry> entries = entryMap.get(key);
            if(entries == null){
                entries = new LinkedList<>();
                entryMap.put(key, entries);
            }
            entries.add(entry);
            // 判断条数是否过大
            entryCount ++;
            if(entryCount > maxEntrySize){
                throw new TooMuchEntryException("zip压缩包最大文件个为：" + maxEntrySize);
            }
        }
    }


    /**
     * 通过文件后缀名称(不加.字符)获取zip包条目
     * @param fileSuffix
     * @return
     */
    public Iterator<ZipEntry> iterator(String fileSuffix){
        List<ZipEntry> list = entryMap.get(fileSuffix);
        if(list == null) return (Iterator)Collections.emptyList().iterator();
        else return list.iterator();
    }

    /**
     * 通过zip条目获取文件的流
     * @param zipEntry
     * @return
     */
    public InputStream getInputStreamOfEntry(ZipEntry zipEntry) throws IOException {
        return zipFile.getInputStream(zipEntry);
    }

    @Override
    public void close() throws IOException {
        if(zipFile != null){
            zipFile.close();
            zipFile = null;
        }
        if(tempFile != null){
            tempFile.delete();
            tempFile = null;
        }
        entryMap = Collections.EMPTY_MAP;
    }

    /**
     * 获取指定后缀的文件保存到container中
     * @param fileSuffix
     * @param container
     */
    public void fillZipEntries(String fileSuffix, List<ZipEntry> container){
        List<ZipEntry> entries = entryMap.get(fileSuffix);
        if(entries != null) container.addAll(entries);
    }

    /**
     * 将解压的文件保存到文件夹中
     * @param folder 文件夹
     * @param isOver true：覆盖存在文件；false：不覆盖存在文件
     */
    public void saveFolder(File folder, boolean isOver) throws IOException {
        byte[] buffer = new byte[2028];
        for (List<ZipEntry> values : entryMap.values()) {
            for (ZipEntry value : values) {
                File file = new File(folder, value.getName());
                if(file.exists() && isOver){
                    // 覆盖
                    try(FileOutputStream outputStream = new FileOutputStream(file)){
                        if(isOver) transfer(zipFile.getInputStream(value), outputStream, buffer);
                    }
                }else{
                    // 不存在
                    File innerFolder = file.getParentFile();
                    if(!innerFolder.exists()){
                        innerFolder.mkdirs();
                    }
                    file.createNewFile();
                    try(FileOutputStream outputStream = new FileOutputStream(file)){
                        transfer(zipFile.getInputStream(value), outputStream, buffer);
                    }
                }
            }
        }
    }

    private void transfer(InputStream input, OutputStream output, byte[] buffer) throws IOException {
        int count = 0;
        while((count = input.read(buffer)) != -1){
            output.write(buffer, 0, count);
        }
    }

    /**
     * 解压到固定的文件夹
     * @param stream zip文件流
     * @param folder 文件夹
     * @param isOver true：覆盖存在文件；false：不覆盖存在文件
     */
    public static void decompress(InputStream stream, File folder, boolean isOver) throws IOException, TooMuchEntryException {
        try (ZipParser parser = new ZipParser(stream, Integer.MAX_VALUE)){
            parser.saveFolder(folder, isOver);
        }
    }

}
